﻿using System;
using FakeItEasy;
using NJabber.Stream;
using NJabber.Stream.Elements;
using NUnit.Framework;
using NJabber.Client.StanzaFactories;

namespace NJabber.Client
{
    [TestFixture]
    public class RosterUpdaterTests
    {
        private RosterCache rosterCache;
        private RosterUpdater updater;
        private IXmppConnection connection;
        private IStanzaStream stanzaStream;
        private IRosterStanzaFactory factory;
        private readonly JID clientJid = "client@jid.com";

        [SetUp]
        public void SetUp()
        {
            connection = A.Fake<IXmppConnection>();
            stanzaStream = A.Fake<IStanzaStream>();
            A.CallTo(() => connection.Stream).Returns(stanzaStream);
            A.CallTo(() => connection.Jid).Returns(clientJid);
            factory = new RosterStanzaFactory();
            rosterCache = new RosterCache(A.Fake<IRosterSynchronizer>());
            updater = new RosterUpdater(connection, factory);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Update_RosterChangesIsNull_ThrowsException()
        {
            updater.Update(null);
        }

        [Test]
        public void Update_ChangesContainsAddedItem_SendsRosterAddRequest()
        {
            RosterItem newItem = new RosterItem("item", "item@jid.org");
            rosterCache.Add(newItem);
            Stanza expectedStanza = factory.CreateAddRosterItemRequest(newItem, clientJid);

            VerifyRosterUpdate(expectedStanza);
        }

        [Test]
        public void Update_ChangesContainsChangedItem_SendsRosterUpdateRequest()
        {
            RosterItem item = new RosterItem("some-item", "item@jid.org");
            rosterCache.Reset(new[] {item});            
            rosterCache["item@jid.org"].Name = "item";
            
            CollectionAssert.IsNotEmpty(rosterCache.ChangedItems);
            Stanza expectedStanza = factory.CreateUpdateRosterItemRequest(null, rosterCache["item@jid.org"], clientJid);

            VerifyRosterUpdate(expectedStanza);
        }

        [Test]
        public void Update_ChangesContainsRemovedItem_SendsRosterDeleteRequest()
        {
            RosterItem item = new RosterItem("some-item", "item@jid.org");
            rosterCache.Reset(new[] { item });
            rosterCache.Remove(item.Jid);
            Stanza expectedStanza = factory.CreateDeleteRosterItemRequest(item, clientJid);

            VerifyRosterUpdate(expectedStanza);
        }

        private void VerifyRosterUpdate(Stanza expectedStanza)
        {
            updater.Update(rosterCache.Changes);
            A.CallTo(() => stanzaStream.Write(expectedStanza)).MustHaveHappened();
        }
    }
}
